﻿using System;
using System.Collections;
using System.Collections.Generic;
using Newtonsoft.Json;

namespace Hsenl {
    [Serializable]
    public abstract class DBField {
        protected static HashSet<Type> allowedTypes = new HashSet<Type>() {
            typeof(int),
            typeof(long),
            typeof(float),
            typeof(double),
            typeof(bool),
            typeof(string),
            typeof(DateTime),
            typeof(DBDocument),
        };

        [JsonProperty]
        protected string fieldName;

        [JsonIgnore]
        public string FieldName => this.fieldName;

        public DBField() { }

        protected virtual bool CheckValueValid<T>(T value) {
            switch (value) {
                case null:
                    return true;
                case string:
                    return true;
                case int:
                    return true;
                case long:
                    return true;
                case float:
                    return true;
                case double:
                    return true;
                case bool:
                    return true;
                case DateTime:
                    return true;
                case DBDocument:
                    return true;
                default: {
                    if (value is IList ilist) {
                        var genericType = ilist.GetType().GetGenericArguments()[0];
                        if (allowedTypes.Contains(genericType)) {
                            return true;
                        }
                    }

                    if (value is IDictionary idict) {
                        var type = idict.GetType();
                        var keyType = type.GetGenericArguments()[0];
                        var valueType = type.GetGenericArguments()[1];
                        if (allowedTypes.Contains(keyType) && allowedTypes.Contains(valueType)) {
                            return true;
                        }
                    }

                    break;
                }
            }

            throw new ArgumentException($"This value type '{value}' is not supported");
        }

        public abstract T GetValue<T>();
        public abstract void SetValue<T>(T value);
    }

    [Serializable]
    public sealed class DBField<T> : DBField {
        [JsonProperty]
        private T _fieldValue;

        [JsonIgnore]
        public T FieldValue => this._fieldValue;

        public DBField(string fieldName, T fieldValue) {
            if (string.IsNullOrEmpty(fieldName))
                throw new ArgumentNullException(nameof(fieldName));

            this.CheckValueValid(fieldValue);

            this.fieldName = fieldName;
            this._fieldValue = fieldValue;
        }

        public override TValue GetValue<TValue>() {
            if (this._fieldValue is not TValue t)
                throw new InvalidCastException($"{this._fieldValue} cast to '{typeof(TValue)}' fail!");

            return t;
        }

        public override void SetValue<TValue>(TValue value) {
            if (value is not T t)
                throw new InvalidCastException($"{value} cast to '{typeof(T)}' fail!");

            this._fieldValue = t;
        }
    }
}